﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace com.hyrentals.data
{
    public enum RentalRequestStateType : byte
    {
        Created_Pending_Owner_Accept = 0,
        Active_Pending_Charge_SecurityDeposit = 1,
        Active_Pending_Charge_Insurance = 2,
        Active_Insurance_Payment_Error = 3,
        Active = 4,
        PickingUp_Pending_Renter_Confirm_FeeCharge = 10,
        PickingUp_Pending_Owner_Submit_FeeCharge = 11,
        PickingUp_Dispute = 12,
        PickingUp_Pending_Charge_RentalFee = 13,
        PickingUp_Payment_Error = 14,
        PickedUp = 15,
        Returning_Pending_Renter_Confirm_Invoice = 20,
        Returning_Pending_Owner_Submit_Invoice = 21,
        Returning_Dispute = 22,
        Returning_Pending_Charge = 23,
        Fulfilled = 30,
        Fulfilling_Payment_Error = 31,
        Expired = 40,
        Rejected = 41,
        Aborted = 42,
        Cancelling_NoShow_Pending_RefundCharge = 50,
        Cancelling_NoShow_Payment_Error = 51,
        Cancelled_NoShow = 52,
        Cancelled = 60,
        Cancelling_From_ActivePendingChargeInsurance_Payment_Error = 61,
        Cancelling_From_Active_Payment_Error = 62,

    }

    public enum RentalRequestEventType : byte
    {
        Expired = 0,
        Accepted_By_Owner = 1,
        Rejected_By_Owner = 2,
        MakeCharge_For_SecurityDeposit = 3,
        Upload_InsuranceProof_7Days_AheadOfPickUp = 4,
        MakeCharge_For_Insurance = 5,
        Cancel = 6,
        Handle_Insurance_Payment_Error = 7,
        Handle_Cancel_From_ActivePendingChargeInsurance_Payment_Error = 8,
        FeeCharge_Confirmed_By_Owner = 9,
        NoShow = 10,
        Handle_Cancel_From_Active_Payment_Error = 11,
        RefundCharge_For_NoShow = 12,
        Handle_Cancel_NoShow_Payment_Error = 13,
        FeeCharge_Confirmed_By_Renter = 14,
        FeeCharge_Declined_By_Renter = 15,
        FeeCharge_Submitted_By_Owner = 16,
        Charge_RentalFee = 17,
        Invoice_Confirmed_By_Owner = 18,
        Invoice_Confirmed_By_Renter = 19,
        Invoice_Declined_By_Renter = 20,
        Invoice_Submitted_By_Owner =21,
        Charge_Invoice = 22,
    }

    public enum RentalRequestEventSenderType : byte
    {
        All = 0,
        Renter = 1,
        Owner = 2,
        System = 3,
    }

    public delegate bool RentalRequestEventHandler(FSM_RentalRequest_Event eventData);
    
    public class FSM_RentalRequest_Event
    {
        public RentalRequestEventType EventType { get; set; }
        public RentalRequestEventSenderType EventSenderType { get; set; } 
        public object EventData { get; set; }
    }

    public class FSM_RentalRequest_State_Event
    {
        public RentalRequestEventType EventType { get; set; }
        public event RentalRequestEventHandler Func;
        public RentalRequestStateType StateOfSuccess { get; set; }
        public RentalRequestStateType StateOfFailure { get; set; }

        public bool Fire(FSM_RentalRequest_Event eventData)
        {
            return Func(eventData);
        }
    }

    public class FSM_RentalRequest_State
    {
        public RentalRequestStateType State { get; set; }
        public FSM_RentalRequest_State_Event[] EventTable { get; set; }
    }

    public class FSM_RentalRequest
    {
        private static FSM_RentalRequest_State[] StateTables;
        // current state
        public RentalRequestStateType State;

        private const int RENTAL_REQUEST_STATE_COUNT = 16;
        public static void Init()
        {
            StateTables = new FSM_RentalRequest_State[RENTAL_REQUEST_STATE_COUNT];

            int stateIndex = 0;

            #region RentalRequestStateType.Created_Pending_Owner_Accept

            FSM_RentalRequest_State state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Created_Pending_Owner_Accept,
                EventTable = new FSM_RentalRequest_State_Event[3]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Expired,
                StateOfSuccess = RentalRequestStateType.Expired,
                StateOfFailure = RentalRequestStateType.Created_Pending_Owner_Accept
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.Expired);

            state.EventTable[1] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Rejected_By_Owner,
                StateOfSuccess = RentalRequestStateType.Rejected,
                StateOfFailure = RentalRequestStateType.Created_Pending_Owner_Accept
            };
            state.EventTable[1].Func += new RentalRequestEventHandler(RentalRequestAction.Reject);

            state.EventTable[2] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Accepted_By_Owner,
                StateOfSuccess = RentalRequestStateType.Active_Pending_Charge_SecurityDeposit,
                StateOfFailure = RentalRequestStateType.Created_Pending_Owner_Accept
            };
            state.EventTable[2].Func += new RentalRequestEventHandler(RentalRequestAction.Accept);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Active_Pending_Charge_SecurityDeposit

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Active_Pending_Charge_SecurityDeposit,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.MakeCharge_For_SecurityDeposit,
                StateOfSuccess = RentalRequestStateType.Active_Pending_Charge_Insurance,
                StateOfFailure = RentalRequestStateType.Aborted
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.MakeChargeForSecurityDeposit);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Active_Pending_Charge_Insurance

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Active_Pending_Charge_Insurance,
                EventTable = new FSM_RentalRequest_State_Event[3]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Upload_InsuranceProof_7Days_AheadOfPickUp,
                StateOfSuccess = RentalRequestStateType.Active,
                StateOfFailure = RentalRequestStateType.Active_Pending_Charge_Insurance
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.UploadInsuranceProof);

            state.EventTable[1] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.MakeCharge_For_Insurance,
                StateOfSuccess = RentalRequestStateType.Active,
                StateOfFailure = RentalRequestStateType.Active_Insurance_Payment_Error
            };
            state.EventTable[1].Func += new RentalRequestEventHandler(RentalRequestAction.MakeChargeForInsurance);

            state.EventTable[2] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Cancel,
                StateOfSuccess = RentalRequestStateType.Cancelled,
                StateOfFailure = RentalRequestStateType.Cancelling_From_ActivePendingChargeInsurance_Payment_Error
            };
            state.EventTable[2].Func += new RentalRequestEventHandler(RentalRequestAction.CancelFromActivePendingChargeInsurance);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Active_Insurance_Payment_Error

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Active_Insurance_Payment_Error,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Handle_Insurance_Payment_Error,
                StateOfSuccess = RentalRequestStateType.Active,
                StateOfFailure = RentalRequestStateType.Aborted
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.HandleInsurancePaymentError);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Cancelling_From_ActivePendingChargeInsurance_Payment_Error

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Cancelling_From_ActivePendingChargeInsurance_Payment_Error,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Handle_Cancel_From_ActivePendingChargeInsurance_Payment_Error,
                StateOfSuccess = RentalRequestStateType.Cancelled,
                StateOfFailure = RentalRequestStateType.Cancelling_From_ActivePendingChargeInsurance_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.HandleCancelFromActivePendingChargeInsurancePaymentError);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Active

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Active,
                EventTable = new FSM_RentalRequest_State_Event[3]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.FeeCharge_Confirmed_By_Owner,
                StateOfSuccess = RentalRequestStateType.PickingUp_Pending_Renter_Confirm_FeeCharge,
                StateOfFailure = RentalRequestStateType.Active
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.FeeChargeConfirmedByOwner);

            state.EventTable[1] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.NoShow,
                StateOfSuccess = RentalRequestStateType.Cancelling_NoShow_Pending_RefundCharge,
                StateOfFailure = RentalRequestStateType.Active
            };
            state.EventTable[1].Func += new RentalRequestEventHandler(RentalRequestAction.NoShowRenterConfirm);

            state.EventTable[2] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Cancel,
                StateOfSuccess = RentalRequestStateType.Cancelled,
                StateOfFailure = RentalRequestStateType.Cancelling_From_Active_Payment_Error
            };
            state.EventTable[2].Func += new RentalRequestEventHandler(RentalRequestAction.CancelFromActive);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Cancelling_From_Active_Payment_Error

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Cancelling_From_Active_Payment_Error,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Handle_Cancel_From_Active_Payment_Error,
                StateOfSuccess = RentalRequestStateType.Cancelled,
                StateOfFailure = RentalRequestStateType.Cancelling_From_Active_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.HandleCancelFromActivePaymentError);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Cancelling_NoShow_Pending_RefundCharge

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Cancelling_NoShow_Pending_RefundCharge,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.RefundCharge_For_NoShow,
                StateOfSuccess = RentalRequestStateType.Cancelled_NoShow,
                StateOfFailure = RentalRequestStateType.Cancelling_NoShow_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.RefundChargeForNoShow);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Cancelling_NoShow_Payment_Error

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Cancelling_NoShow_Payment_Error,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };

            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Handle_Cancel_NoShow_Payment_Error,
                StateOfSuccess = RentalRequestStateType.Cancelled_NoShow,
                StateOfFailure = RentalRequestStateType.Cancelling_NoShow_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.HandleCancelNoShowPaymentError);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.PickingUp_Pending_Renter_Confirm_FeeCharge

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.PickingUp_Pending_Renter_Confirm_FeeCharge,
                EventTable = new FSM_RentalRequest_State_Event[2]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.FeeCharge_Confirmed_By_Renter,
                StateOfSuccess = RentalRequestStateType.PickingUp_Pending_Owner_Submit_FeeCharge,
                StateOfFailure = RentalRequestStateType.PickingUp_Pending_Renter_Confirm_FeeCharge
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.FeeChargeConfirmedByRenter);

            state.EventTable[1] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.FeeCharge_Declined_By_Renter,
                StateOfSuccess = RentalRequestStateType.PickingUp_Dispute,
                StateOfFailure = RentalRequestStateType.PickingUp_Pending_Renter_Confirm_FeeCharge
            };
            state.EventTable[1].Func += new RentalRequestEventHandler(RentalRequestAction.FeeChargeDeclinedByRenter);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.PickingUp_Pending_Owner_Submit_FeeCharge

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.PickingUp_Pending_Owner_Submit_FeeCharge,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.FeeCharge_Submitted_By_Owner,
                StateOfSuccess = RentalRequestStateType.PickingUp_Pending_Charge_RentalFee,
                StateOfFailure = RentalRequestStateType.PickingUp_Pending_Owner_Submit_FeeCharge
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.FeeChargeSubmittedByOwner);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.PickingUp_Pending_Charge_RentalFee

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.PickingUp_Pending_Charge_RentalFee,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Charge_RentalFee,
                StateOfSuccess = RentalRequestStateType.PickedUp,
                StateOfFailure = RentalRequestStateType.PickingUp_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.ChargeRentalFee);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.PickedUp

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.PickedUp,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Invoice_Confirmed_By_Owner,
                StateOfSuccess = RentalRequestStateType.Returning_Pending_Renter_Confirm_Invoice,
                StateOfFailure = RentalRequestStateType.PickedUp
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.InvoiceConfirmedByOwner);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Returning_Pending_Renter_Confirm_Invoice

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Returning_Pending_Renter_Confirm_Invoice,
                EventTable = new FSM_RentalRequest_State_Event[2]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Invoice_Confirmed_By_Renter,
                StateOfSuccess = RentalRequestStateType.Returning_Pending_Owner_Submit_Invoice,
                StateOfFailure = RentalRequestStateType.Returning_Pending_Renter_Confirm_Invoice
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.InvoiceConfirmedByRenter);

            state.EventTable[1] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Invoice_Declined_By_Renter,
                StateOfSuccess = RentalRequestStateType.Returning_Dispute,
                StateOfFailure = RentalRequestStateType.Returning_Pending_Renter_Confirm_Invoice
            };
            state.EventTable[1].Func += new RentalRequestEventHandler(RentalRequestAction.InvoiceDeclinedByRenter);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Returning_Pending_Owner_Submit_Invoice

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Returning_Pending_Owner_Submit_Invoice,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Invoice_Submitted_By_Owner,
                StateOfSuccess = RentalRequestStateType.Returning_Pending_Charge,
                StateOfFailure = RentalRequestStateType.Returning_Pending_Owner_Submit_Invoice
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.InvoiceSubmittedByOwner);

            StateTables[stateIndex++] = state;

            #endregion

            #region RentalRequestStateType.Returning_Pending_Charge

            state = new FSM_RentalRequest_State
            {
                State = RentalRequestStateType.Returning_Pending_Charge,
                EventTable = new FSM_RentalRequest_State_Event[1]
            };
            state.EventTable[0] = new FSM_RentalRequest_State_Event
            {
                EventType = RentalRequestEventType.Charge_Invoice,
                StateOfSuccess = RentalRequestStateType.Fulfilled,
                StateOfFailure = RentalRequestStateType.Fulfilling_Payment_Error
            };
            state.EventTable[0].Func += new RentalRequestEventHandler(RentalRequestAction.ChargeInvoice);

            StateTables[stateIndex++] = state;

            #endregion
            
        }

        public void FireEvent(FSM_RentalRequest fsm, FSM_RentalRequest_Event eventData)
        {
            FSM_RentalRequest_State currentState = null;
            foreach (FSM_RentalRequest_State state in StateTables)
            {
                if (state.State == fsm.State)
                {
                    currentState = state;
                }
            }

            if (currentState == null)
                return;

            foreach (FSM_RentalRequest_State_Event stateEvent in currentState.EventTable)
            {
                if (stateEvent.EventType == eventData.EventType)
                {
                    bool result = stateEvent.Fire(eventData);
                    if (result == true)
                    {
                        fsm.State = stateEvent.StateOfSuccess;
                    }
                    else
                    {
                        fsm.State = stateEvent.StateOfFailure;
                    }
                    return;
                }
            }
        }

       
    }
}
